

module sdram_new #(
    parameter tRC = 6,  // 60 两次刷新或两次激活之间的间隔
    parameter tRP = 2,  // 18 预充电到激活的间隔
    parameter tRCD = 2,  // 18 激活到读写之间的间隔
    parameter tDPL = 2,  // 12 数据输入到预充电间隔
    parameter tDAL = 3,  // 30 数据输入到激活/刷新间隔
    parameter tMRD = 2,  // 模式寄存器配置时间
    parameter tREF = 64_000_00,  // 刷新周期时间
    parameter tRAS = 5,  // 激活到预充电
    parameter tCAS = 3'd2,
    parameter CS_NUM = 2,
    parameter PRE_DELAY = 7,
    parameter FORCE_REF_CYCLE = (tRC + 1) * 8192 + 10000
) (
    input clk,
    input clk_ref,
    input rst_n,

    //ar
    input  [4 : 0] s_axi_arid,
    input  [ 31:0] s_axi_araddr,
    input  [7 : 0] s_axi_arlen,
    input  [2 : 0] s_axi_arsize,
    input  [1 : 0] s_axi_arburst,
    input          s_axi_arlock,
    input  [3 : 0] s_axi_arcache,
    input  [2 : 0] s_axi_arprot,
    input          s_axi_arvalid,
    output         s_axi_arready,
    //r
    output [4 : 0] s_axi_rid,
    output [ 31:0] s_axi_rdata,
    output [1 : 0] s_axi_rresp,
    output         s_axi_rlast,
    output         s_axi_rvalid,
    input          s_axi_rready,
    //aw
    input  [4 : 0] s_axi_awid,
    input  [ 31:0] s_axi_awaddr,
    input  [7 : 0] s_axi_awlen,
    input  [2 : 0] s_axi_awsize,
    input  [1 : 0] s_axi_awburst,
    input          s_axi_awlock,
    input  [3 : 0] s_axi_awcache,
    input  [2 : 0] s_axi_awprot,
    input          s_axi_awvalid,
    output         s_axi_awready,
    //w
    input  [ 31:0] s_axi_wdata,
    input  [3 : 0] s_axi_wstrb,
    input          s_axi_wlast,
    input          s_axi_wvalid,
    output         s_axi_wready,
    //b
    output [4 : 0] s_axi_bid,
    output [1 : 0] s_axi_bresp,
    output         s_axi_bvalid,
    input          s_axi_bready,

    output [12:0] dram_addr,
    output [ 1:0] dram_ba,
    output        dram_cas_n,
    output        dram_cke,
    output        dram_clk,
    output [ 1:0] dram_cs_n,
    output        dram_ras_n,
    output        dram_we_n,
    inout  [31:0] dram_dq,
    output [ 1:0] dram_dqml,
    output [ 1:0] dram_dqmh,
    output        init_end
);

typedef enum  { INIT_PRE, INIT_REF1, INIT_REF2, INIT_MRS, IDLE, NOP, READ, WRITE, PRE, REF } state_t;
// ras, cas, we
localparam [2:0] cmd_nop    = 3'b111;
localparam [2:0] cmd_act    = 3'b011;
localparam [2:0] cmd_write  = 3'b100;
localparam [2:0] cmd_read   = 3'b101;
localparam [2:0] cmd_mrs    = 3'b000;
localparam [2:0] cmd_pre    = 3'b010;
localparam [2:0] cmd_ref    = 3'b001;

state_t state, state_d, nop_state_q, nop_state_d;

logic [1:0] cs_q, cs_d;
logic [2:0] cmd_q, cmd_d;
logic [1:0] ba_q, ba_d;
logic [12:0] addr_q, addr_d;
logic [9:0] col_q, col_d;
logic [4:0] id_q, id_d;
logic [3:0] len_q, len_d;
logic we_q, we_d;
logic [31:0] data_q, data_d;
logic [3:0] mask_q, mask_d;
logic [13:0] counter_q, counter_d;
logic init_end_r;
logic [31:0] wdata;
logic wvalid;
logic wready;
logic wlast;
logic rvalid;
logic refresh;
logic [9:0] ref_counter;
logic [1:0] read_stall_num;

logic [2:0] cas_counter;
logic [3:0] rlen;
logic rreq;
logic [ 2:0] remain_cnt;
logic [4:0] rid, bid;
logic bvalid;

wire counter_z = counter_q == 0;
wire arvalid = s_axi_arvalid & ~rreq;
wire awvalid = s_axi_awvalid & ~bvalid;
assign init_end = init_end_r;

assign dram_cke     = 1'b1;
assign dram_clk     = clk_ref;
assign dram_cs_n    = cs_q;
assign dram_ras_n   = cmd_q[2];
assign dram_cas_n   = cmd_q[1];
assign dram_we_n    = cmd_q[0];
assign dram_addr    = addr_q;
assign dram_ba      = ba_q;
assign dram_dq      = we_q ? data_q : {32{1'bz}};
assign {dram_dqmh, dram_dqml} = mask_q;


always_ff @(posedge clk)begin
    if(~rst_n)begin
        state       <= NOP;
        nop_state_q <= INIT_PRE;
        counter_q   <= 10000;
        cmd_q       <= cmd_nop;
        cs_q        <= 2'b0;
        ba_q        <= 0;
        addr_q      <= 0;
        id_q        <= 0;
        col_q       <= 0;
        len_q       <= 0;
        data_q      <= 0;
        mask_q      <= 4'b0;
        we_q        <= 0;
        init_end_r  <= 0;
    end
    else begin
        state       <= state_d;
        nop_state_q <= nop_state_d;
        counter_q   <= counter_d;
        cmd_q       <= cmd_d;
        cs_q        <= cs_d;
        addr_q      <= addr_d;
        ba_q        <= ba_d;
        col_q       <= col_d;
        id_q        <= id_d;
        data_q      <= data_d;
        mask_q      <= mask_d;
        we_q        <= we_d;
        len_q       <= len_d;
        if(state == IDLE)begin
            init_end_r <= 1'b1;
        end
    end
end

always_comb begin
    counter_d = counter_q;
    state_d = state;
    nop_state_d = nop_state_q;
    cmd_d = cmd_q;
    cs_d = cs_q;
    addr_d = addr_q;
    data_d = data_q;
    we_d = we_q;
    ba_d = ba_q;
    col_d = col_q;
    id_d = id_q;
    len_d = len_q;
    if(s_axi_wvalid & s_axi_wready)begin
        mask_d = ~s_axi_wstrb;
    end
    else if((state == IDLE) & ~refresh & ~awvalid & arvalid)begin
        mask_d = 4'b0;
    end
    else begin
        mask_d = mask_q;
    end
    case(state)
    NOP: begin
        counter_d = counter_q - 1;
        if(counter_d == 0) state_d = nop_state_q;
        cmd_d = cmd_nop;
        we_d = 1'b0;
    end
    INIT_PRE: begin
        counter_d = tRP - 1;
        state_d = NOP;
        nop_state_d = INIT_REF1;
        cmd_d = cmd_pre;
        addr_d[10] = 1'b1;
    end
    INIT_REF1: begin
        counter_d = tRC - 1;
        state_d = NOP;
        nop_state_d = INIT_REF2;
        cmd_d = cmd_ref;
        addr_d[10] = 1'b0;
    end
    INIT_REF2: begin
        counter_d = tRC - 1;
        state_d = NOP;
        nop_state_d = INIT_MRS;
        cmd_d = cmd_ref;
    end
    INIT_MRS: begin
        counter_d = tMRD - 1;
        state_d = NOP;
        cmd_d = cmd_mrs;
        nop_state_d = IDLE;
        addr_d = {7'b0, tCAS, 4'b0};
        ba_d = 0;
    end
    IDLE: begin
        if(refresh)begin
            state_d = NOP;
            nop_state_d = REF;
            counter_d = tRP;
            cmd_d = cmd_pre;
            addr_d[10] = 1'b1;
            cs_d = 2'b0;
        end
        else if(arvalid | awvalid)begin
            state_d = NOP;
            nop_state_d = awvalid ? WRITE : READ;
            counter_d = tRCD - 1;
            cmd_d = cmd_act;
            if(awvalid) begin
                cs_d = s_axi_awaddr[27];
                {ba_d, addr_d} = s_axi_awaddr[26:12];
                col_d = s_axi_awaddr[11:2];
                id_d = s_axi_awid;
                len_d = s_axi_awlen[3:0];
            end
            else begin
                cs_d = s_axi_araddr[27];
                {ba_d, addr_d} = s_axi_araddr[26:12];
                col_d = s_axi_araddr[11:2];
                id_d = s_axi_arid;
                len_d = s_axi_arlen[3:0];
            end
        end
    end
    READ: begin
        len_d = len_d - 1;
        col_d = col_d + 1;
        if(len_q == 0)begin
            if(read_stall_num != 0)begin
                state_d = NOP;
                nop_state_d = PRE;
                counter_d = read_stall_num;
            end
            else begin
                state_d = PRE;
            end
        end
        cmd_d = cmd_read;
        addr_d = {3'b0, col_q};
    end
    WRITE: begin
        if(wvalid)begin
            state_d = NOP;
            nop_state_d = WRITE;
            counter_d = 1;
            col_d = col_d + 1;
        end
        else if(~wvalid & wlast)begin
            state_d = NOP;
            nop_state_d = PRE;
            counter_d = tDPL - 1;
        end
        cmd_d = wvalid ? cmd_write : cmd_nop;
        we_d = 1'b1;
        data_d = wdata;
        addr_d = {3'b0, col_q};
    end
    PRE: begin
        state_d = NOP;
        nop_state_d = IDLE;
        counter_d = tRP - 1;
        cmd_d = cmd_pre;
    end
    REF: begin
        state_d = NOP;
        nop_state_d = IDLE;
        counter_d = tRC - 1;
        cmd_d = cmd_ref;
        cs_d = 2'b0;
    end
    endcase
end


wire rdata_hsk = s_axi_rvalid & s_axi_rready;

always_ff @(posedge clk)begin
    if(~rst_n)begin
        cas_counter <= 0;
        rlen <= 0;
        rreq <= 0;
        rid <= 0;
        bid <= 0;
        wlast <= 0;
        wready <= 0;
        bvalid <= 1'b0;
        read_stall_num <= 0;
    end
    else begin
        if(cas_counter != 0) cas_counter <= cas_counter - 1;
        if(rdata_hsk)begin
            rlen <= rlen - 1;
        end
        if(state == IDLE && arvalid && !awvalid && !refresh)begin
            rlen <= s_axi_arlen[3:0];
            cas_counter <= tRCD + tCAS - 1;
            rreq <= 1'b1;
            rid <= s_axi_arid;
            read_stall_num <= s_axi_arlen[3:0] > tCAS - (tMRD + 1) ? 0 : tCAS - (tMRD + 1) - s_axi_arlen[3:0];
        end
        if(state == IDLE && !refresh && !bvalid && awvalid)begin
            bid <= s_axi_awid;
        end
        if(s_axi_rvalid & s_axi_rready & s_axi_rlast)begin
            rreq <= 1'b0;
        end
        if((state == IDLE) & ~refresh & awvalid) wlast <= 1'b0;
        if(s_axi_wvalid & s_axi_wready & s_axi_wlast) wlast <= 1'b1;

        if(s_axi_wvalid & s_axi_wready & s_axi_wlast) begin
            bvalid <= 1'b1;
        end
        else if(bvalid & s_axi_bready)begin
            bvalid <= 1'b0;
        end

        if(awvalid & s_axi_awready)begin
            wready <= 1'b1;
        end
        else if(s_axi_wvalid & s_axi_wready & s_axi_wlast)begin
            wready <= 1'b0;
        end
    end
end

logic [tCAS-1:0] rwait;
always_ff @(posedge clk_ref)begin
    if(~rst_n)begin
        rwait <= 0;
    end
    else begin
        rwait <= {cmd_q == cmd_read, rwait[tCAS-1:1]};
    end
end

sdram_async_fifo #(4, 32) u_sdram_rfifo (
    .src_clk(clk_ref),
    .src_rst_n(rst_n),
    .src_wdata(dram_dq),
    .src_valid(rwait[0]),
    .dst_clk(clk),
    .dst_rst_n(rst_n),
    .dst_rdata(s_axi_rdata),
    .dst_valid(s_axi_rvalid),
    .dst_ready(s_axi_rready)
);

// sdram_fifo #(4, 32) u_sdram_rfifo (
//     .clk(clk),
//     .rst_n(rst_n),
//     .we(rvalid),
//     .wdata(dram_dq),
//     .remain_cnt(remain_cnt),
//     .re(rdata_hsk),
//     .rvalid(s_axi_rvalid),
//     .rdata(s_axi_rdata)
// );

wire [2:0] wremain_cnt;
sdram_fifo #(4, 32) u_sdram_wfifo (
    .clk(clk),
    .rst_n(rst_n),
    .we(s_axi_wvalid & s_axi_wready),
    .wdata(s_axi_wdata),
    .remain_cnt(wremain_cnt),
    .re((state == WRITE) & wvalid),
    .rvalid(wvalid),
    .rdata(wdata)
);

always_ff @(posedge clk)begin
    if(~rst_n)begin
        ref_counter <= 770;
        refresh <= 0;
    end
    else begin
        if(init_end)begin
            if(ref_counter == 0)begin
                refresh <= 1'b1;
            end
            else begin
                ref_counter <= ref_counter - 1;
            end
        end

        if(state == REF)begin
            ref_counter <= 770;
            refresh <= 1'b0;
        end
    end
end

assign s_axi_arready = (state == IDLE) & ~refresh & ~awvalid & ~rreq;
assign s_axi_awready = (state == IDLE) & ~refresh & ~bvalid;
assign s_axi_wready  = wready;

assign s_axi_rid = rid;
assign s_axi_rlast = rlen == 0;
assign s_axi_rresp = 0;

assign s_axi_bid = bid;
assign s_axi_bvalid = bvalid;
assign s_axi_bresp = 0;

endmodule